#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "log.h"
#include <vector>

#include "dtn/ConexionDTN.h"

dtn_endpoint_id_t* ConexionDTN::parse_eid(dtn_handle_t handle, dtn_endpoint_id_t* eid, const char *str){
    // try the string as an actual dtn eid
    if (!dtn_parse_eid_string(eid, str)) {
	log("%s (literal)\n", str);
	return eid;
    }
    // build a local eid based on the configuration of our dtn
    // router plus the str as demux string
    else if (!dtn_build_local_eid(handle, eid, str)){
	log("%s (local)\n", str);
	return eid;
    }
	
    log("invalid eid string '%s'\n", str);
    exit(1);
}

void ConexionDTN::print_eid(const char *  label, dtn_endpoint_id_t * eid){
    printf("%s [%s]\n", label, eid->uri);
}
    
ConexionDTN::ConexionDTN(std::string nombre){ 
    source_eid = nombre.c_str();
    
    // initialize bundle spec
    memset(&bundle_spec, 0, sizeof(bundle_spec));
}

int ConexionDTN::connect(std::string dst_eid){
    // open the ipc handle
    log("Opening connection to local DTN daemon\n");
	
    int err = dtn_open(&handle);
    if (err != DTN_SUCCESS)
	log(stderr, "fatal error opening dtn handle: %s\n", dtn_strerror(err));
	
    this->dst_eid = dst_eid;
    log("Destination: %s\n", dst_eid.c_str());
    parse_eid(handle, &bundle_spec.dest, dst_eid.c_str());

    log("Source: %s\n", source_eid.c_str());
    parse_eid(handle, &bundle_spec.source, source_eid.c_str());
    //Reply to source
    dtn_copy_eid(&bundle_spec.replyto, &bundle_spec.source);

    return err;
}

int ConexionDTN::send(const char* buffer, size_t size){
    print_eid("source_eid", &bundle_spec.source);
    print_eid("replyto_eid", &bundle_spec.replyto);
    print_eid("dest_eid", &bundle_spec.dest);
    
    // set the dtn options
    bundle_spec.expiration = 3600*48; //48 horas
    bundle_spec.dopts      = 0;
    bundle_spec.priority   = COS_NORMAL;

    dtn_set_payload(&bundle_payload, DTN_PAYLOAD_MEM, (char*)buffer, size);

    memset(&bundle_id, 0, sizeof(bundle_id));

    int ret;
    if ((ret = dtn_send(handle, DTN_REGID_NONE, &bundle_spec, &bundle_payload,
			&bundle_id)) != 0)
        {
            fprintf(stderr, "error sending bundle: %d (%s)\n",
                    ret, dtn_strerror(dtn_errno(handle)));
            return -1;
        }

    log("bundle sent successfully: id %s,%llu.%llu\n",
	bundle_id.source.uri,
	bundle_id.creation_ts.secs,
	bundle_id.creation_ts.seqno);

    return 0;
}

int ConexionDTN::bind(){
    dtn_reg_info_t reginfo;

    memset(&reginfo, 0, sizeof(reginfo));

    // create a new registration based on the eid
    dtn_copy_eid(&reginfo.endpoint, &bundle_spec.source);
    reginfo.regid             = DTN_REGID_NONE;
    reginfo.expiration        = 3;
    reginfo.flags             = DTN_REG_DEFER;
    reginfo.script.script_val = (char*)"";
    reginfo.script.script_len = 0;

    dtn_reg_id_t regid	= DTN_REGID_NONE;// registration id

    if (dtn_register(handle, &reginfo, &regid) != 0) {
	fprintf(stderr, "error creating registration: (%s)\n",
		dtn_strerror(dtn_errno(handle)));
	exit(1);
    }

    printf("register succeeded, regid %d\n", regid);

    return 0;
}

void ConexionDTN::receive(std::vector<char> &buffer){
    std::string nil;
    ConexionDTN::receive(buffer, nil);
}

void ConexionDTN::receive(std::vector<char> &buffer, std::string &uri){

    buffer.clear();
    dtn_bundle_spec_t spec;
    dtn_bundle_payload_t payload;

    memset(&spec, 0, sizeof(spec));
    memset(&payload, 0, sizeof(payload));
    printf("dtn_recv [%s]...\n", bundle_spec.source.uri);
    int ret;
    while ((ret = dtn_recv(handle, &spec, DTN_PAYLOAD_MEM, &payload, 
			360000)) < 0)
        {
            fprintf(stderr, "error getting recv reply: %d (%s)\n",
                    ret, dtn_strerror(dtn_errno(handle)));
	    sleep(5);
	    //exit(1);
        }

    printf("%d payload bytes from [%s]: transit time=%d ms\n",
	   payload.buf.buf_len,
	   spec.source.uri, 0);

    uri = spec.source.uri;
    buffer.insert(buffer.begin(), payload.buf.buf_val, payload.buf.buf_val+payload.buf.buf_len);
    buffer[payload.buf.buf_len] = 0;

    dtn_free_payload(&payload);
    if (spec.blocks.blocks_len > 0) {
	free(spec.blocks.blocks_val);
	spec.blocks.blocks_val = NULL;
	spec.blocks.blocks_len = 0;
    }
}
    
int ConexionDTN::disconnect(){
    return dtn_close(handle);
}
